﻿using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using fCloud.SensorApi.Helper;
using fCloud.SensorApi.Models;
using MQTTnet;
using MQTTnet.Protocol;

namespace fCloud.SensorApi.Services;

public interface IReceiverService
{
    public Task SubscribeAsync(string topic);
}

public partial class ReceiverService:IReceiverService
{
    private readonly IMqttClient _client;
    private readonly IMysqlDbService _mysqlDbService;
    private  List<Sensor> _sensors = new();
    private  List<Device> _devices = new();
    //private readonly ISseService _sseService;
    private readonly ILogger<IReceiverService> _logger;
    private readonly IRedisService _redisService;
    private readonly IInfluxDbService _influxDbService;
    public ReceiverService(IConfiguration config,IMysqlDbService mysqlDbService,ILogger<IReceiverService> logger,IRedisService redisService,IInfluxDbService influxDbService)
    {
        _logger = logger;
        _redisService = redisService;
        _influxDbService = influxDbService;
        //_sseService = sseService;
        _mysqlDbService=mysqlDbService;

        _ = GetDataFromMysql();
        
        var options = new MqttClientOptionsBuilder()
            .WithTcpServer(config["Mqtt:Host"],int.Parse(config["Mqtt:Port"]!))
            .WithClientId(Guid.NewGuid().ToString())
            .WithCredentials(config["Mqtt:Username"],config["Mqtt:Password"])
            .Build();
        _client = new MqttClientFactory().CreateMqttClient();
        _client.ConnectAsync(options).GetAwaiter().GetResult();
        // 建议添加连接恢复逻辑
        _client.DisconnectedAsync += async e =>
        {
            if (e.Reason == MqttClientDisconnectReason.NormalDisconnection) return;
    
            await Task.Delay(TimeSpan.FromSeconds(5));
            await _client.ConnectAsync(options);
        };
        
    }

    private async Task GetDataFromMysql()
    {
        _sensors = await  _mysqlDbService.GetSensors();
        _devices = await _mysqlDbService.GetDevices();
        var topics = _sensors.Select(x => x.Sensortopic).ToList();
        Task.Run(async () =>
        {
            await SubScribeAllSensorTopic(topics);
            //await SubscribeAsync("YH050014/up");
        });
        _client.ApplicationMessageReceivedAsync += HandleMessageReceivedAysnc;
    }

    public async Task SubscribeAsync(string topic)
    {
        var subscribeOptions = new MqttClientSubscribeOptionsBuilder()
            .WithTopicFilter(f=>f.WithTopic(topic))
            .Build();
        var result = await _client.SubscribeAsync(subscribeOptions);
        if (result.Items.Any(x => x.ResultCode != MqttClientSubscribeResultCode.GrantedQoS0))
        {
            _logger.LogError("订阅失败");
        }
    }

    private async Task SubScribeAllSensorTopic(List<string> topics)
    {
        var subscribeOptions = new MqttClientSubscribeOptionsBuilder();
        foreach (var topic in topics)
        {
            subscribeOptions.WithTopicFilter(f => f
                .WithTopic(topic)
                .WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce));
        }
        var result = await _client.SubscribeAsync(subscribeOptions.Build());
        // 增强结果检查
        var failedTopics = result.Items
            .Where(x => x.ResultCode > MqttClientSubscribeResultCode.GrantedQoS2)
            .Select((item, index) => new { Topic = topics[index], item.ResultCode })
            .ToList();

        if (failedTopics.Count != 0)
        {
            throw new InvalidOperationException($"订阅失败的主题: { 
                string.Join(", ", failedTopics.Select(x => $"{x.Topic}({x.ResultCode})")) 
            }");
        }
    }
    private static double GetDoubleValueRequired(
        IEnumerable<DataPoint> dataList, 
        string key) 
    { 
        var dataPoint = dataList.FirstOrDefault(x => x.Key == key) ?? new DataPoint()
        {
            Key = key,
            Data = "0.0"
        };
        if (!double.TryParse(dataPoint.Data?.ToString(), out double value))
        {
            throw new FormatException($"{key} 值格式错误: {dataPoint.Data}");
        }
        return value;
    }

    private Task HandleMessageReceivedAysnc(MqttApplicationMessageReceivedEventArgs e)
    {
        var payload = "";
        try
        {
            var message = e.ApplicationMessage;
            var srcPayload=Encoding.UTF8.GetString(message.Payload).Trim('\uFEFF');
            //清理接收消息字符串
            payload = JsonCleaner.CleanTruncatedJson(srcPayload);
          
            if (!payload.Contains("report_type") || !payload.Contains("203")) 
                return Task.CompletedTask;
            var options = new JsonSerializerOptions 
            {
                AllowTrailingCommas = true,
                ReadCommentHandling = JsonCommentHandling.Skip
            };
            var reportType = JsonSerializer.Deserialize<ReportData>(payload, options);
            var dens = GetDoubleValueRequired(reportType!.Data.DataList, "Dens");
            var sensor = _sensors.FirstOrDefault(i => i.Sensortopic == message.Topic);
            //var device = _devices.FirstOrDefault(i => i.Mac == reportType!.Data.Mac);
            var sensorData = new SensorData
            {
                Id = Guid.NewGuid(),
                SensorSn = sensor!.SensorCode,
                DeviceCode = sensor!.SensorCode,
                DeviceName = sensor!.SensorCode,
                CreateTime = DateTime.Now,
                Temper = GetDoubleValueRequired(reportType!.Data.DataList, "Temp"),
                Dens = (dens>100)?dens/1000:dens,
                Vis = GetDoubleValueRequired(reportType!.Data.DataList, "Vis"),
                Vis40 = GetDoubleValueRequired(reportType!.Data.DataList, "Vis40"),
                Diel = GetDoubleValueRequired(reportType!.Data.DataList, "Diel"),
                Ppm = GetDoubleValueRequired(reportType!.Data.DataList, "PPM"),
                Aw = GetDoubleValueRequired(reportType!.Data.DataList, "Aw"),
                Mo = GetDoubleValueRequired(reportType!.Data.DataList, "Mo"),
                Par1 = GetDoubleValueRequired(reportType!.Data.DataList, "Conc4um"),
                Par2 = GetDoubleValueRequired(reportType!.Data.DataList, "Conc6um"),
                Par3 = GetDoubleValueRequired(reportType!.Data.DataList, "Conc14um"),
                Par4 = GetDoubleValueRequired(reportType!.Data.DataList, "Conc21um"),
                Class1 = GetDoubleValueRequired(reportType!.Data.DataList, "ISO4um"),
                Class2 = GetDoubleValueRequired(reportType!.Data.DataList, "ISO6um"),
                Class3 = GetDoubleValueRequired(reportType!.Data.DataList, "ISO14um"),
                Class4 = GetDoubleValueRequired(reportType!.Data.DataList, "ISO21um"),
                Fe1 = GetDoubleValueRequired(reportType!.Data.DataList, "Fe1"),
                Fe2 = GetDoubleValueRequired(reportType!.Data.DataList, "Fe2"),
                Fe3 = GetDoubleValueRequired(reportType!.Data.DataList, "Fe3"),
                Fe4 = GetDoubleValueRequired(reportType!.Data.DataList, "Fe4"),
                Fe5 = GetDoubleValueRequired(reportType!.Data.DataList, "Fe5"),
                Fe6 = GetDoubleValueRequired(reportType!.Data.DataList, "Fe6"),
                Fe7 = GetDoubleValueRequired(reportType!.Data.DataList, "Fe7"),
                Fe8 = GetDoubleValueRequired(reportType!.Data.DataList, "Fe8"),
                NFe1 = GetDoubleValueRequired(reportType!.Data.DataList, "NFe1"),
                NFe2 = GetDoubleValueRequired(reportType!.Data.DataList, "NFe2"),
                NFe3 = GetDoubleValueRequired(reportType!.Data.DataList, "NFe3"),
                NFe4 = GetDoubleValueRequired(reportType!.Data.DataList, "NFe4"),
                NFe5 = GetDoubleValueRequired(reportType!.Data.DataList, "NFe5"),
            };
            _mysqlDbService.InsertSensorData(sensorData);
            _redisService.InsertSensorDataAsync(sensorData);
            _influxDbService.WriteDataAsync(sensorData);
            //_ = _sseService.PushDataAsync(sensorData);
            //Console.WriteLine($"topic:{message.Topic},context:{payload}");
        }
        catch (JsonException ex)
        {
            _logger.LogError("JSON解析失败 |原始数据: {Payload} | 错误: {ExMessage}",payload, ex.Message);
            // 可添加重试或错误处理逻辑
        }
        catch (Exception ex)
        {
            _logger.LogError("消息处理异常: {ExMessage}\n{ExStackTrace}", ex.Message, ex.StackTrace);
        }
        
        return Task.CompletedTask;
    }
    
}